讓我們一起使用 Xcode 內建的 Git 來進行版本控制吧
隕石開發的時間,常常會碰到,某個功能做到一半不做了,跑去開發另一個功能,而這個新功能還沒做好,就又被要求把之前能夠運作的版本打包成 .ipa 給團隊測試,又或者是在某個時期又被要求回朔之前的某個版本。
面對這種版本反反覆覆的切換,這時我只能說:「Git 在手,希望無窮」,還好有使用 Git,不然每次隕石落下時真的求助無門,再也不怕隕石落下了。
說到程式設計,你通常會聽到大家都會說要記得用 Git,那 Git 又是什麼呢?簡單來說,我們在開發期間需要 Git 來幫我們進行 「版本控制」。我們通常會在創建專案時,將 Git 初始化到我們專案內使用,之後我們就能對我們的專案進行版本控制了。
當專案使用 Git 進行版控後,你可以很輕易的控制專案目前的版本,無論是回朔到哪個版本上,只要有進行 commit 的動作,永遠不用怕沒辦法恢復。你還能可以藉由 Git 與他人共同開發同一個專案,使用分支(branch)的方式來進行不同功能的開發,到最後將各自的版本合併到同一個專案中。
很幸運的,身為一個 iOS 工程師,我們現在可以在 Xcode 裡面直接使用 Git 的功能來對我們專案進行版本控制,不需要另外打開我們的終端機寫 command line,它還能夠連結線上的 GitHub 帳戶,將專案推到遠端存儲庫上。
如果想要更了解 git 或是深入其原理,可以參考下面幾個連結:
本篇文章主要會使用 Xcode 內建的 git 的來做演示。
我們使用 Git & Github 大致上的流程會像下圖這樣:
簡單來說工作目錄(Working Directory)就是目前的專案所在目錄,存放這我們專案的檔案內容,而當我們執行 git add
指令時,這些被新增的檔案會被放入暫存區(Staging Area)暫存著,直到我們最後執行 git commmit
的時候,這些被新增的檔案才是真正的被放到本地存儲庫( Local repository),也就是說執行了 git commit
後才算是完成整個流程,也就是提交了一個版本。
而當我們想要把本地存儲庫的內容傳送上去遠端儲存庫時,我們會使用 git push
將本地內容推上遠端,這時本地與遠端的儲存庫也就同步了。反之,如果今天遠端存儲庫更新了,我們可以使用 git pull
來將遠端的內容加入到本地的存儲庫,這樣我們本地的內容也與遠端同步了。
上面提到的 Git 指令也就是可以再終端機裡面使用的指令,上面提到的 git add
、git commit
、git push
和 git pull
會是我們在使用 Git 中常使用到的幾個指令。當然還有其他更多複雜操作的指令,但是我們主要會使用 Xcode 來幫我們做上述的操作,不需要再額外打開終端機使用。
如果有興趣的讀者也能夠去深入瞭解我們在 Xcode 上做的操作是對應 git 中的哪個指令,這能夠使你對於 Xcode 背後幫我們執行了哪些動作。
當我們建立一個新專案的時候,再決定專案存放位置時,Xcode 就會詢問你要不要創建一個 git 的 repository。如果想要在我們專案上進行版本控制就勾選這個項目,這時 git 就會被加入到我們的專案中了。
但是如果忘了勾選也沒有關係,我們可以在建立專案後在 Xcode 上方工具列的 Source Control 裡面選擇 Create Git Repositories 就可以了:
![](https://i.imgur.com/ucQVHjf.png =400x300)
現在讓我們對我們的專案進行一些更動,你可以在任何地方加上指令。在這邊首先我們先新增一個 Swift file,我們會在裡面定義一個 message 的常數。
接著我們到我們的 ViewController
的 viewDidLoad
的位置加上一個 print
將我們剛剛定義的 message
打印出來:
這時候你會看到 Xcode 左側檔案目錄下的某些文件右方會有些標記,這邊簡單介紹一下常見的標籤內容:
- ' ':(空白) 沒有更動檔案
- 'M':本地更動檔案
- 'U':存儲庫更新的檔案
- 'A':本地新增檔案
- 'D':本地刪除檔案
- 'I':被 git 忽略的檔案
- 'R':該檔案在存儲庫中被更換
- '-':該檔案有多種狀態,顯示內容以查看狀態
- '?':沒有在版本控制內的檔案
你可以點選在 Xcode 右上方的 Version Editor(雙向箭頭按鈕),來比較本次更動的內容的差別:
這時候我們打開 Version Editor,這時我們螢幕上會有兩個畫面,左邊是我們目前更動過後的版本,而右邊是之前的版本,你可以藉由這種方式來查看前後的差異。
你會發現中間有一個按鈕,點下去之後會出現放棄更改(Discard changes):
這時如果我們點選它之後,Xcode 會詢問你是否確認要放棄更改:
如果我們選擇 Discard 後,你會發現你的程式碼恢復到之前版本的狀態,也就是還沒更動前的狀態,檔案右邊 M 的標記也會跟著消失:
接下來就是最重要的步驟了,我們需要來將這些更動給 commit,如此一來才算是整個流程完成,我們到 Source Control 中選擇 Commit,這時候會跳出一個新視窗:
這個視窗左側的目錄是本次有更動的檔案,而勾選的檔案代表著這次要被 提交(commit)的對象,如果不想要記錄在這次提交中可以取消勾選。而在右側的視窗有點類似於 Version Editor 的功能,你可以在這兩個視窗內比較前後版本的差異,當然也可以做放棄更改的動作。
而下方輸入框就是用來輸入我們提交的訊息,而這個訊息就是用來描述本次更動的內容,簡單扼要地說明即可,完成之後按下 Commit 來提交本次版本。
如果想要知道版本變動的歷史紀錄,可以點下 Version Editor 並且選擇 Log。
這時我們在畫面右側可以看到我們提交的歷史紀錄,你還可以點選 Show modified file 來查看更動的檔案:
你可能會好奇,而為什麼要建立分支呢?你可以想像我們可以從主分支將我們原本的專案複製一份到新的分支上做新功能的開發,就算開發到一半有問題會導致程式 crush 或是尚未完成,但我們主要分支上的專案依舊保持著原有的程式碼,我們只需要切回主分支上即可。而當我們之後新功能開發完畢後再合併回主分支即可,如此一來就不用害怕在開發期間沒有能夠正常運作的版本。
接下來讓我們來看 Xcode 中如何建立 git 的 branch 吧,首先我們到 Source Control Navigator,其中有三個資料夾:
首先我們打開 Branches 右鍵點選我們的 master 選擇 Branch from "master" :
之後我們會來創建我們的分支,你可以再 Branch 中為你的分支命名,在遮邊你可以根據你要開發的 功能 來命名你的分支:
接著你會發現我們剛剛新建的 branch 變成 current,也表示我們目前跳轉到新的分支上了,同時你也能夠在右邊視窗看到每個 branch 在哪個 commit 進度上:
接著讓我們再剛剛所新建的 branch 上做點事情吧,在這邊我們只在 viewDidLoad 加上一行 view.backgroundColor = .black
:
接著我們將這次變動 commit。而這次提交的 commit 會記錄在目前的分支上,也就是剛剛我們建立的 branch:
而我們的 master 依舊保持在原有的 commit 上:
接著如果我們想要變回原本的版本的話,我們只需要對我們原有的主分支 (master)按右鍵,選擇 Checkout,這時我們專案也會切回到主分支上:
所以這時我們的 ViewController 是看不到我們剛剛所新增的 view.backgroundColor = .black
程式碼:
而如果你想要把 changeBackgroundColor 上更改的內容合併到 master 分支上的話,這時候我們可以對我們的 branch 按右鍵,選擇用 Merge 的合併他們,因為我們這邊是要把 changeBackgroundColor 更改內容合併到 master,所以我們在這邊選擇 Merge "changeBackgroundColor" into "master" :
使用 Merge 合併之後,你會看到我們 master 分支的標籤移動最新的 commit 的上,這時 changeBackgroundColor 與 master 的版本也同步了。
為什麼會有衝突?原因是可能某一個檔案同時間被更改了,所以在合併的時候會發生所謂的衝突,而這時候我們就必須要選擇保留哪一個版本(同時也捨棄掉另一個版本),而在 Xcode 中合併發生衝突時,系統會跳出一個視窗:
而會出現這次合併的檔案,其中檔案後面有標記 C 的項目表示有衝突(Conflict),因此我們必須對這衝突的檔案做一些取捨。這個畫面有點類似於我們在 commit 時會出現前後對比的畫面,而這邊我們是對有衝突的檔案進行比較,而這時候我們可以透過中間的按鈕來決定要保留哪邊的程式碼:
這邊比較特別的是 Xcode 除了可以選擇左右以外,他還能夠同時選取兩邊的程式碼,只是將程式碼放在不同順序,像是這邊我們選擇 Choose Left Then Right 的話會如下圖顯示(你可以透過中間的半圓形來判斷選取順序):
選擇好之後我們就可以按下 commit 來做本次合併的提交。
若你還沒有 Github 的讀者,記得先申請 Github 帳號。在這邊我們可以在 Xcode 的 Preferences 的 Accounts,點選下方 + 號按鈕,這時候可以選擇我們所要新增的平台帳號:
很幸運的,我們也能夠在 Xcode 中在 Github 上建立遠端存儲庫,首先我們一樣到 Source Control Navigator 對 Remotes 資料夾點選右鍵,選擇 Create "Xcode GitDemo" Remote...:
這時會跳出一個視窗,你可以對你的專案進行一些描述和設定,完成之後按下 Created:
按下 Create 之後你會發現我們 Remotes 多了一個 origin,而這時候會發現 Xcode 同時也把我們 master 分支的內容推到了遠端存儲庫,我們可以對它按右鍵選擇 View on Github... 來在 Github 上瀏覽:
其實在一開始建立遠端存儲庫的時候,Xcode 同時已經偷偷的幫我們 push 到遠端存儲庫一次了,所以在我們建立完成之後會在遠端存儲庫也看到我們本地的內容。而我們如果想要更新遠端存儲庫的話,我們一樣在 Source Control 選擇 Push 來把本地的內容推至遠端:
你可以選擇要推到遠端的哪個分支上:
在提交的時候,你同時也可以將本次提交的內容 push 到遠端分支上:
而更新本地也是相同原理,我們一樣在 Source Control 選擇 Pull , 來將我們遠端資料庫的內容更新到本地上:
你也能夠選擇要 pull 哪個遠端的分支:
如果沒有本地內容與遠端相同的話,Xcode 會出現提示視窗告訴你目前版本已經是最新版:
如果想要知道目前本地及遠端存儲庫的狀態,我們可以點選 Source Control 的 Fetch and Refresh Status 來刷新狀態:
如果你在本地做了一些更改之後,你可以在 Source Control Navigator 看到的分支旁邊有個向上的箭頭,旁邊還有個數字,而這個數字表示提示本地有幾個 commit 需要 push 到遠端更新:
而如果是遠端資料庫有更新,則該分支旁邊會出現箭頭向下的數字,表示有幾個遠端的更動需要更新到本地:
隕石落下後,只好跟工程師們一起搭上時光機再重來一次了 ?
常常會聽到有工程師說:「人生不能重來,但是 Git 可以」,雖然標題很聳動,但是實際上 Git 就是如此強大,無論我們想要回到哪個版本上都能夠回去。所以,Git 是一門需要學會的技術。
當然 Xcode 裡面的 Git 的功能並不是完整的,他還是會有一些沒辦法達到的功能,需要使用 Command line 來執行,但是還是足以應付一般的需求了。
或許會有人不推薦使用 Xcode 或 SourceTree 這種圖形化介面工具(GUI)的方式來操作 Git 的版控,因為你可能不知道你執行了 Git 的哪些指令。可能圖形化介面工具可以讓你點一點就能夠完成想要執行的效果,但實際上他在背後可能下了許多指令,但是我們看不出來。但是如果使用 Command line 的話,每一行指令都是我們所輸入的,我們知道每一行做了什麼,所以得到最後的結果。
但是往另一個方面來思考,兩種方式都能為我們解決相同的問題,而且最後的結果也相同,所以我認為只要能夠解決問題實際上都是好方法。圖形化介面的優點就是能夠透過簡單的操作,就能執行複雜的指令,並將結果顯示在畫面上,讓使用者能夠一目瞭然這些過程,而缺點就是可能背後執行的指令我們看不見;而使用 Command line 的話,因為每一步的指令都是我們下的,我們知道每一個過程,但是對於剛開始學習程式的人可能是一個很大的門檻,而且沒有圖形化的顯示,可能單純透過文字很難讓人產生連結。
所以我自己會推薦從 GUI 的方式學習,之後再透過 Command Line 去理解在這些背後的指令即可。那麼本次教學就到這邊了,希望各位能夠學會如何在 Xcode 使用 Git 來進行版本控制。